package cn.pink.core.support.lock;

import cn.pink.core.config.DeployConfig;
import cn.pink.core.config.IronConfig;
import com.hazelcast.core.Hazelcast;
import com.hazelcast.cp.ISemaphore;
import io.vertx.core.Vertx;
import io.vertx.core.VertxException;
import io.vertx.core.impl.ContextInternal;
import io.vertx.core.shareddata.Lock;
import io.vertx.spi.cluster.hazelcast.impl.HazelcastLock;

import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.concurrent.TimeUnit.NANOSECONDS;

/**
 * @Author: pink
 * @Date: 2022/8/25 11:16
 */
public class IronLock {
    private static Vertx VERTX;

    private static LocalLock localLock;

    public static void init(Vertx vertx) {
        VERTX = vertx;

        if(IronConfig.COMMON_DEPLOY == DeployConfig.SIMPLE) {
            localLock = new LocalLock();
        }
    }

    public static Lock lock(String name, long timeout) {
        if(IronConfig.COMMON_DEPLOY != DeployConfig.SIMPLE) {
            ISemaphore iSemaphore = Hazelcast.getHazelcastInstanceByName(IronConfig.COMMON_GAME).getCPSubsystem().getSemaphore("__vertx." + name);
            boolean locked = false;
            long remaining = timeout;
            do {
                long start = System.nanoTime();
                try {
                    locked = iSemaphore.tryAcquire(remaining, TimeUnit.MILLISECONDS);
                } catch (InterruptedException e) {
                    // OK continue
                }
                remaining = remaining - MILLISECONDS.convert(System.nanoTime() - start, NANOSECONDS);
            } while (!locked && remaining > 0);
            if (locked) {
                return new HazelcastLock(iSemaphore, Executors.newCachedThreadPool(r -> new Thread(r, "iron-vertx-hazelcast-service-release-lock-thread")));
            } else {
                throw new VertxException("Timed out waiting to get lock " + name);
            }
        }
        else {
            return localLock.acquire((ContextInternal) VERTX.getOrCreateContext(), name, timeout);
        }
    }
}
